<?php

namespace App\Services;

use App\Common\Libs\ArrayHelper;
use App\Common\Libs\IdBuilder;
use App\Consts\GlobalCacheConst;
use App\Consts\GlobalConst;
use App\Consts\GlobalCounterConst;
use App\Consts\GlobalUserRecommendConst;
use App\Events\UserRecommend\CancelUserRecommendEvent;
use App\Events\UserRecommend\CreateUserRecommendEvent;
use App\Exceptions\Error;
use App\Models\Cache\Cache;
use App\Models\Cache\CacheKey;
use App\Models\UserRecommend;
use Illuminate\Support\Facades\DB;

class UserRecommendService extends BaseService
{
    private $_repository;

    public function __construct()
    {
        parent::__construct();
        $this->_repository = repository()->UserRecommendRepository;
    }

    public function createUserRecommend($loadData)
    {
        // 防止连点
        $alreadyRecommend = $this->judgeAlreadyRecommendOrCancelFromCache($loadData['user_id'], $loadData['target_id']);
        if ($alreadyRecommend) {
            return true;
        }

        DB::beginTransaction();
        try {
            // 缓存没有命中，查看推荐记录是否存在
            $userRecommend = $this->_repository->getUserRecommendByTargetId($loadData['user_id'], $loadData['target_id']);
            if ($userRecommend) {
                // 用户已经推荐，更新用户推荐缓存以防止重复推荐
                if ($userRecommend && $userRecommend->is_deleted == GlobalConst::IS_NOT_DELETE) {
                    $this->saveUserRecommendCache($userRecommend);
                    return $userRecommend;
                }

                // 用户取消了推荐，更新记录
                $this->_repository->updateUserRecommend($userRecommend, ['is_deleted' => GlobalConst::IS_NOT_DELETE]);
            } else {
                $loadData = ArrayHelper::merge($loadData, [
                    'recommend_id' => IdBuilder::getUniqueID(),
                ]);

                // 新增【用户推荐】
                $userRecommend = $this->_repository->createUserRecommend($loadData);
                if (!$userRecommend) {
                    return Error::handle();
                }
            }

            // 同步更新主体计数、用户计数
            $this->syncCounterFromCache($loadData['user_id'], $loadData['target_id'], $loadData['target_type']);

            // 写入缓存，防止连点
            $this->saveUserRecommendCache($userRecommend);

            /**
             * 队列做以下几件事：
             *  1. 修改推荐卡片用户头像区域
             *  2. 记录日志
             *  3. 生成动态
             */
            CreateUserRecommendEvent::dispatch($userRecommend);

            DB::commit();

            return $userRecommend;
        } catch (\Exception $e) {
            DB::rollBack();
            return Error::handle(lang('Unstable network'));
        }
    }

    public function cancelUserRecommendByTargetId($loadData)
    {
        // 防止连点
//        $alreadyCancelRecommend = $this->judgeAlreadyRecommendOrCancelFromCache($loadData['user_id'], $loadData['target_id'], true);
//        if ($alreadyCancelRecommend) {
//            return true;
//        }

        DB::beginTransaction();
        try {
            $userRecommend = $this->_repository->getUserRecommendByTargetId($loadData['user_id'], $loadData['target_id']);
            if ($userRecommend && $userRecommend->is_deleted == GlobalConst::IS_DELETED) {
                $this->saveCancelUserRecommendCache($userRecommend);
                return true;
            }

            // 更新用户推荐记录
            $this->_repository->updateUserRecommend($userRecommend, ['is_deleted' => GlobalConst::IS_DELETED]);

            // 同步更新主体计数、用户计数
            $this->syncCounterFromCache($loadData['user_id'], $loadData['target_id'], $loadData['target_type']);

            // 写入缓存，防止连点
            $this->saveCancelUserRecommendCache($userRecommend);

            /**
             * 队列做以下几件事：
             *  1. 修改推荐卡片用户头像区域
             *  2. 记录日志
             *  3. 删除动态
             */
            CancelUserRecommendEvent::dispatch($userRecommend);

            DB::commit();

            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            return Error::handle(lang('Unstable network'));
        }
    }

    /**
     * 新增/取消推荐同步更新主体计数、用户计数缓存
     * @param $userId
     * @param $targetId
     * @param $targetType
     * @param int $countNum
     * @return bool
     */
    public function syncCounterFromCache($userId, $targetId, $targetType, $countNum = GlobalCounterConst::INCREASE)
    {
        // 用户计数
        Cache::getInstance([
            'user_id' => $userId
        ])->incrFromArray(CacheKey::COUNTER_USER, GlobalCounterConst::COUNT_USER_RECOMMEND, $countNum);

        // 主体计数
        $keyParams = [];
        $cacheKey = '';
        if ($targetType == GlobalConst::TARGET_TYPE_ARTICLE) {
            $keyParams = ['article_id' => $targetId];
            $cacheKey = CacheKey::COUNTER_ARTICLE;
        }

        if (!$cacheKey) {
            return false;
        }

        Cache::getInstance($keyParams)->incrFromArray($cacheKey, GlobalCounterConst::COUNT_USER_RECOMMEND, $countNum);

        return true;
    }

    /**
     * 队列记录日志
     * @param UserRecommend $userRecommend
     * @param false $isCancel
     * @return bool
     */
    public function saveUserRecommendLogCounterByQueue($userRecommend, $isCancel = false)
    {
        $actionType = GlobalCounterConst::ACTION_TYPE_RECOMMEND;
        if ($isCancel) {
            $actionType = GlobalCounterConst::ACTION_TYPE_CANCEL_RECOMMEND;
        }

        $logData = [
            'send_user_id' => $userRecommend->user_id,
            'receive_user_id' => $userRecommend->user_id,
            'action_id' => $userRecommend->recommend_id,
            'action_type' => $actionType,
            'target_id' => $userRecommend->target_id,
            'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
            'created_at' => time(),
        ];
        service()->LogCountService->createLogByQueue($logData);

        return true;
    }

    public function judgeUserIsRecommend($userId, $targetId)
    {
        if (!$userId) {
            return GlobalUserRecommendConst::USER_IS_NOT_RECOMMEND;
        }
        $recommend = $this->_repository->getUserRecommendByTargetId($userId, $targetId);

        return $recommend && $recommend->is_deleted == GlobalConst::IS_NOT_DELETE ? GlobalUserRecommendConst::USER_IS_RECOMMEND : GlobalUserRecommendConst::USER_IS_NOT_RECOMMEND;
    }

    /**
     * 从缓存中验证用户对主体是否推荐过，或取消过推荐
     * @param $userId
     * @param $targetId
     * @param false $isCancel
     * @return mixed
     */
    public function judgeAlreadyRecommendOrCancelFromCache($userId, $targetId, $isCancel = false)
    {
        $keyParams = [
            'user_id' => $userId,
            'target_id' => $targetId,
        ];

        if ($isCancel) {
            $cacheKey = CacheKey::CANCEL_USER_RECOMMEND_TARGET;
        } else {
            $cacheKey = CacheKey::USER_RECOMMEND_TARGET;
        }

        return Cache::getInstance($keyParams)->get($cacheKey);
    }

    /**
     * 更新用户推荐缓存
     *  1.推荐信息入缓存
     *  2.清除取消推荐信息缓存
     * @param UserRecommend $userRecommend
     * @return bool
     */
    public function saveUserRecommendCache($userRecommend)
    {
        $keyParams = [
            'user_id' => $userRecommend->user_id,
            'target_id' => $userRecommend->target_id,
        ];

        Cache::getInstance($keyParams)->set(CacheKey::USER_RECOMMEND_TARGET, true, GlobalCacheConst::EXPIRED_SIXTY);

        Cache::getInstance($keyParams)->delete(CacheKey::CANCEL_USER_RECOMMEND_TARGET);

        return true;
    }

    /**
     * 更新用户取消推荐缓存
     *  1.清除推荐信息缓存
     *  2.取消推荐信息入缓存
     * @param UserRecommend $userRecommend
     * @return bool
     */
    public function saveCancelUserRecommendCache(UserRecommend $userRecommend)
    {
        $keyParams = [
            'user_id' => $userRecommend->user_id,
            'target_id' => $userRecommend->target_id,
        ];

        Cache::getInstance($keyParams)->set(CacheKey::CANCEL_USER_RECOMMEND_TARGET, true, GlobalCacheConst::EXPIRED_SIXTY);

        Cache::getInstance($keyParams)->delete(CacheKey::USER_RECOMMEND_TARGET);

        return true;
    }

    /**
     * 获取用户推荐的主体的用户
     * @param $targetId
     * @param $targetType
     * @return bool|string
     */
    public function getUserRecommendTargetUserId($targetId, $targetType)
    {
        $receiveUserId = '';
        if ($targetType == GlobalConst::TARGET_TYPE_ARTICLE) {
            $article = repository()->ArticleRepository->getArticleByArticleId($targetId);
            if ($article) {
                $receiveUserId = $article['author_id'];
            }
        }

        return $receiveUserId ?? false;
    }
}
